(function(global) {

	function extend(d, s) {
		for (var p in s) {
			if (Object.prototype.hasOwnProperty.call(s, p)) {
				d[p] = s[p];
			}
		}
	}
	function AjaxHistory(callback, options) {
		this.callback = callback;
		var me = this;
		this.checkState = function() {
			checkState.call(me);
		};
		extend(this, AjaxHistory.defaultOptions);
		extend(this, options);
		this._stopped = true;
		if (AjaxHistory.hashchange > 0) {
			try {
				var fire = function(event) {
					if (!me._stopped) {
						me.fire(me.currentHash, me.get());
					}
				};
				if (AjaxHistory.hashchange == 1) {
					global.addEventListener("hashchange", fire, false);
				}
				else {
					global.attachEvent("onhashchange", fire);
				}
				return;
			} catch (e) {}
		}
		this.checkState();
	}

	function checkState() {
		var oldHash = this.currentHash;
		var newHash = this.get(this.window);
		if (oldHash != newHash) {
			this.fire(oldHash, newHash);
		}
		this.start();
	}
	extend(AjaxHistory.prototype, {
		get: function get() {
			var hash = decodeURIComponent(this.window.location.href.split('#')[1] || '');
			this.currentHash = hash;
			return hash;
		},
		set: function set(hash) {
			this.currentHash = hash;
			this.window.location.hash = encodeURIComponent(hash);
			return this;
		},
		setData: function setData(hash, data) {
			var oldHash = this.currentHash;
			this.setHash(hash);
			this.data = data;
			this.fire.call(this, oldHash, hash);
			return this;
		},
		getData: function getData() {
			return this.data;
		},
		fire: function fire(oldHash, newHash) {
			this.currentHash = newHash;
			return this.callback.call(this.data, newHash, oldHash, this);
		},
		stop: function stop() {
			this._stopped = true;
			global.clearTimeout(this._timeout);
			return this;
		},
		start: function start() {
			if (AjaxHistory.hashchange > 0 || !this._stopped) {
				return this;
			}
			this._stopped = false;
			this._timeout = global.setTimeout(this.checkState, this.interval);
			return this;
		}
	});
	extend(AjaxHistory, {
		defaultOptions: {
			data: undefined,
			interval: 100,
			currentHash: '',
			window: self
		},
		hashchange: (function() {
			var sup = false, win = global, test = function() { sup = true };
			try {
				if (win.addEventListener && win.dispatchEvent) {
					win.addEventListener("hashchange", test, false);
					var evt = document.createEvent("HTMLEvents");
					evt.initEvent('hashchange', false, false);
					win.dispatchEvent(evt);
					win.removeEventListener("hashchange", test, false);
					return sup ? 1 : 0;
				}
				else {
					win.attachEvent("onhashchange", test);
					win.fireEvent("onhashchange");
					win.detachEvent("onhashchange", test);
					return sup ? 2 : 0;
				}
			} catch (e) { console.log(e) }
			return 0;
		})()
	});

	// create ProtoLove if not exists
	global.ProtoLove = global.ProtoLove || {};
	// export AjaxHistory
	global,ProtoLove.AjaxHistory = AjaxHistory;
	global.AjaxHistory = AjaxHistory;
	
	var get = null,
		set = null,
		setData = null,
		getData = null,
		stop = null;

})(this);